#include <sys/types.h>
#include <sys/socket.h>

#include "search_mod.h"
#include "sockutil.h"



typedef struct ServiceSocket
{	
	char 			servicename[32];
	int 			conSock;
}ServiceSocket;

ServiceSocket svrsocklist[2] = {("",0), ("",0)};

typedef struct ModuleConfig
{
	const char* host;
	unsigned short port;
}ModuleConfig;


static const command_rec search_cmds[] = 
{
	AP_INIT_TAKE1(CONFIG_OPTION_HOST, (__take1)search_set_config_option_host, NULL, RSRC_CONF, "get config host"),
	AP_INIT_TAKE1(CONFIG_OPTION_PORT, (__take1)search_set_config_option_port, NULL, RSRC_CONF, "get config port"),
	{ NULL}	
};



/* declare the module */
module  AP_MODULE_DECLARE_DATA search_module = {
	STANDARD20_MODULE_STUFF,
	NULL,
	NULL,
	search_create_server_conf,
	NULL,
	search_cmds,
	search_hooks,
};


/* register the  module */
static void search_hooks(apr_pool_t *p)
{
	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
	{
		ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p, "in %s: install the signal SIGPIPE error!", MODULE_NAME);	
	}
	ap_hook_handler(search_handler, NULL, NULL, APR_HOOK_MIDDLE);	
}

/* Create config rec for host, alloc memory for config */
static void* search_create_server_conf(apr_pool_t* pool, server_rec *s)
{	
	ModuleConfig* pConfig = (ModuleConfig *)apr_pcalloc(pool, sizeof(ModuleConfig));	
	//struct tt* pConfig = (struct tt *)apr_pcalloc(pool, sizeof(tt));	
	return pConfig;
}

/* read director or option from http.conficommand_rec, set config value  */
static const char* search_set_config_option_host(cmd_parms* parms, void *cfg, const char* arg)
{
	//struct tt* pConfig = (struct tt *)ap_get_module_config(parms->server->module_config, &my_module);
	ModuleConfig* pConfig = (ModuleConfig *)ap_get_module_config(parms->server->module_config, &search_module);
	pConfig->host= arg;
	ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, parms->pool, "in %s: search_set_config_file(), get server is %s",
		MODULE_NAME, pConfig->host);	
	return NULL;
}

static const char* search_set_config_option_port(cmd_parms* parms, void *cfg, const char* arg)
{
	//struct tt* pConfig = (struct tt *)ap_get_module_config(parms->server->module_config, &my_module);
	ModuleConfig* pConfig = (ModuleConfig *)ap_get_module_config(parms->server->module_config, &search_module);
	pConfig->port = atoi(arg);
	ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, parms->pool, "in %s: search_set_config_file(), get port is %d",
		MODULE_NAME, pConfig->port);	
	return NULL;
}



/* 
 * read configure, init the module, init socket, connect to server
 */
static int init(request_rec*  r)
{
	connSocket = socket(AF_INET, SOCK_STREAM, 0);
	if( connSocket < 0 )
	{
		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s : init(), create socket error, errno = %d, erro = %s ", 
			MODULE_NAME, errno, strerror(errno));
		return -1;
	}
	
	ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "in %s : init(), connSocket = %d ", MODULE_NAME, connSocket);
	ModuleConfig* pConfig = (ModuleConfig *)ap_get_module_config(r->server->module_config,  &search_module);	
	ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "in %s : init(), host = %s",MODULE_NAME, pConfig->host);
	ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "in %s : init(), port = %d",MODULE_NAME, pConfig->port);

	int ret = tcp_connect(connSocket, pConfig->host, pConfig->port, DEFAULT_TIMEOUT);	
	
	return ret;
}

/* read post data  */
/*
static int read_post_data(request_rec* r, char* c_buf, apr_size_t* data_size)
{
	int ret = 0 ;
	apr_size_t read_offset = 0 ;

	ret = ap_setup_client_block(r,REQUEST_CHUNKED_ERROR);
	if (ret)
		return ret;

	if ((ret = ap_should_client_block(r)))
	{
		apr_size_t remaining = r->remaining;
		if (remaining > (*data_size))
		{
			 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "in %s: read_post_data(), now %d chars from client, too large ", 
				MODULE_NAME, (int)remaining);		
		} 
		if (remaining < (*data_size))
		{
			*data_size =remaining;
		}
		while( read_offset < *data_size )
		{
			ret = ap_get_client_block(r,(char*)c_buf + read_offset, *data_size - read_offset);
			if ( ret <= 0 )
			{
				break;  
			}
			read_offset += ret;
		}
		*data_size = read_offset;
		if (ret < 0 )
			return ret;
	} 
	return 0;
}
*/

/* handler of module, it is the core function */
static int search_handler(request_rec* r)
{
	if( strcmp(r->uri, REQUEST_URI) == 0) 
	{
		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s :  handler(), OK uri = %s", MODULE_NAME, r->uri);      
	}

	if( strcmp(r->uri, REQUEST_URI2) == 0) 
	{
		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s :  handler(), OK 2 uri = %s", MODULE_NAME, r->uri);      
	}

	if( strcmp(r->uri, REQUEST_URI) && strcmp(r->uri, REQUEST_URI2) )
	{
		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s :  handler(), uri = %s", MODULE_NAME, r->uri);      
		return DECLINED;
	}
	if(r->method_number != MODULE_METHOD)
	{
		ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "in %s : handler(), HTTP_METHOD_NOT_ALLOWED, method_num = %d", 
			MODULE_NAME, r->method_number);      
		return HTTP_METHOD_NOT_ALLOWED;
	}	

	int ret = 0;
	
	/* init connSocket */
	if( connSocket == 0 )
	{
		ret = init(r);
		if(ret)
		{
			ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s: handler(), init error ", MODULE_NAME);
			handle_error(r, ERROR_MESSAGE, "handler(), init error");
			return DONE;
		}
		else
		{
			ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "in %s: handler(), init ok, connSocket = %d", 
				MODULE_NAME, connSocket);
		}
	}
	
	/* read post data 
	const char *lenp = apr_table_get(r->headers_in, "Content-Length");
	if( !lenp)
	{
		ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "in %s: handler() can't get content length ",MODULE_NAME);      
		return DECLINED;
	}


	unsigned int  nContentLength = (unsigned int)apr_atoi64(lenp);
    if (0 == nContentLength || nContentLength > (MAX_DATA_SIZE))
    {
		ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "in %s: handler() nContentLength = %d, error ",
			 MODULE_NAME, nContentLength);      
        return DECLINED;
    }
	ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "in %s: handler() get content length = %d", MODULE_NAME, nContentLength);      
	int bufLen = nContentLength + 1;
    char* postdata = (char *)apr_palloc(r->pool, bufLen);
    memset(postdata, '\0', bufLen);
	apr_size_t data_size = nContentLength;	
	ret = read_post_data(r, postdata, &data_size);
	if( ret )
	{
		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s : handler() call read_post_data() error , ret = %d", 
			MODULE_NAME, ret);      
		return DECLINED;
	}
	ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "in %s handler(), call read_post_data, get data = %s : ", 
		MODULE_NAME, postdata);      
	*/
	const char* queryStr = r->args;
	if( queryStr == NULL)
	{
		handle_error(r, "error", "query is null");
		return OK;
	}
	unsigned int len = strlen(queryStr);
	ret = handle(r, queryStr, len);
	return OK;
}

static int handle(request_rec* r, const char* data, unsigned int len)
{
	ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "********************************");      
	ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "********************************");      

	int times = 0;
	while(times < TRY_TIMES)
	{
		times++;
		if( connSocket <= 0 )
		{
			int retInit = init(r);
			if( retInit )
			{
				ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s handle(), call init() failed", MODULE_NAME);
				handle_error(r, ERROR_MESSAGE, " call init() failed");
				break;
			}
		}
		/*1. send data to searchservice */
		int retLen = 0;
		retLen = send_int(connSocket, len, DEFAULT_TIMEOUT);
		if( retLen != sizeof(int))
		{
			ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s handle(), error send_int = %d, retLen = %d", 
				MODULE_NAME, len, retLen);      			
			shutdown(connSocket, SHUT_RDWR);
			connSocket = 0;
			continue;
		}
		
		retLen = safe_send(connSocket, data, len, DEFAULT_TIMEOUT);
		if( retLen != len)	
		{
			ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s handle(), safe_send error sendLen =%d, retLen = %d", 
				MODULE_NAME, len, retLen);      			
			shutdown(connSocket, SHUT_RDWR);
			connSocket = 0;
			continue;
		}

		/*2. recv data from searchsevice */
		int recvLen = recv_int(connSocket, DEFAULT_TIMEOUT);
		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s handle(),get  recvLen = %d ",MODULE_NAME, recvLen); 

		if( recvLen <=0 || recvLen >= MAX_DATA_SIZE )
		{
			ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s handle(), recv_len error recvLen = %d\n", 
				MODULE_NAME, recvLen);      			
			//handle_error(r, ERROR_MESSAGE, "recv_len error recvLen");
			shutdown(connSocket, SHUT_RDWR);
			connSocket = 0;
			continue;
		}
		char *recvbuf = (char *)apr_pcalloc(r->pool, (recvLen+1)*sizeof(char));
		if( recvbuf == NULL)
		{
			continue;
			//return -1;
		}
		bzero(recvbuf, recvLen+1);
		retLen = safe_recv(connSocket, recvbuf, recvLen, DEFAULT_TIMEOUT);
		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s handle(), call safe_recv ,ret len = %d ",
			MODULE_NAME, retLen); 
		if(retLen > 0 && retLen == recvLen)
		{
			recvbuf[recvLen] = '\0';
			ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s handle(), recvLen = %d ok",
				MODULE_NAME, recvLen); 
			ap_rprintf(r,"%s", recvbuf);
			return 0;
		}
		else
		{
			handle_error(r, ERROR_MESSAGE, "recv error");
			return -1;
		}
	}	
	
	return -1;
}

static int handle_error(request_rec* r, const char* message, const char* result)
{
	ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "in %s handle_eror(), message = %s, result = %s",
		MODULE_NAME, message, result); 
	ap_rprintf(r, "%s", message);
	return 0;

}


